from django.shortcuts import render
from rest_framework import status
from rest_framework.views import APIView
from django_redis import get_redis_connection
from rest_framework.response import Response
import base64
import pickle

from carts.serializers import CartDeleteSerializer, CartSelectAllSerializer
from goods.models import SKU
from . import serializers
# Create your views here.

class CartView(APIView):
    def perform_authentication(self, request):
        """
        重写父类的用户验证方法,不在进入视图前就检查JWT
        保证用户未登录也可以进入下面的请求方法,登录与不登录都可以通过身份的验证
        """
        pass

    def post(self,request):
        """新增"""
    #     使用序列化器校验参数
        serializer=serializers.CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
    #     读取校验之后的参数
        sku_id=serializer.validated_data.get("sku_id")
        count=serializer.validated_data.get("count")
        selected=serializer.validated_data.get("selected")
    #     判断用户是否登录
    # request.user 会去查询用户的身份信息.如果未登录就会抛出异常或者指定为匿名用户
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
    #         用户已登录,操作redis
            redis_conn=get_redis_connection("carts")
    #         因为要保存两条数据 所以使用管道
            pl=redis_conn.pipeline()
    #         给购物车的商品的数量进行增量计算
            pl.hincrby("cart_%s" % user.id,sku_id,count)
            # 如果勾选了
            if selected:
    #             记录用户是否勾选
                pl.sadd("cart_selected_%s"% user.id,sku_id)
    #         执行管道
            pl.execute()
    #         响应结果
            return Response(serializer.data,status=status.HTTP_201_CREATED)
        else:
            # 用户未登录,操作cookie
            cart_str= request.COOKIES.get("cart")
    #        判断用户是否存在cookie中有购物车数据
            if cart_str:
    #             说明用户在cookie中操作过购物车数据
    #             将原始的购物车字符串转成bytes类型的字符串(二进制字符串)
    #             cart_str.encode()
    #             将二进制的字符串,转换成base64编码后的二进制字符串
    #             base64.b64decode(cart_str.encode())
    #             将base64编码之后的二进制字符串转换成字典
                cart_dict=pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict={}
    #             判断要添加到cookie中的购物车数据是否存在,如果已存储就累加,反之,赋新值
            if sku_id in cart_dict:
                # 获取这个商品的数量
                origin_count=cart_dict[sku_id]["count"]
                # 把原来的数据进行累加
                count +=origin_count
#             更新字典
            cart_dict[sku_id]={
                # 商品的数量等于累加的数量
                "count" : count,
            #     是否勾选
                "selected" : selected
            }
#             将购物车字典写入到cookie,必须先字典转换成字符串
#             将字典经过pickle序列化为bytes
#             pickle.dump(cart_dict)
#             将bytes经过base64编码为二进制的字符串(二进制字典转二进制字符串)
#             base64.b64encode(pickle.dump(cart_dict))
#             将二进制的字符串转成字符串
            cookie_cart_str=base64.b64encode(pickle.dumps(cart_dict)).decode()
#             生成响应对象
            responce=Response(serializer.data,status=status.HTTP_201_CREATED)
#             写入到cookie
            responce.set_cookie("cart",cookie_cart_str)

            return responce
    def get(self,request):
        #     判断用户是否登录
        # request.user 会去查询用户的身份信息.如果未登录就会抛出异常或者指定为匿名用户
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            #         用户已登录,操作redis
            redis_conn = get_redis_connection("carts")
    #         查询出所有的购物车数据  用户id作为主key 查询商品id 和数量
            redis_cart=redis_conn.hgetall("cart_%s"% user.id)
    #       获取到的都是bytes类型
            cart_selected=redis_conn.smembers("cart_selected_%s"% user.id)
    #         统一查询之后的格式为cart_dict,方便后面的查询sku的操作
            cart_dict={}
            for sku_id,count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    "count" : int(count),
                    # 通过redis的set方法存储的 存的商品都是勾选的
                    "selected" : sku_id in cart_selected
                }
        else:
            # 用户未登录,操作cookie
            cart_str = request.COOKIES.get("cart")
            #        判断用户是否存在cookie中有购物车数据
            if cart_str:
                #             说明用户在cookie中操作过购物车数据
                #             将原始的购物车字符串转成bytes类型的字符串(二进制字符串)
                #             cart_str.encode()
                #             将二进制的字符串,转换成base64编码后的二进制字符串
                #             base64.b64decode(cart_str.encode())
                #             将base64编码之后的二进制字符串转换成字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
    #         遍历cart_dict,取出里面的所有sku_id,用于查询出现购物车数据对应的sku对象
        sku_ids = cart_dict.keys()
        # 这种写法避免遍历了cart_dict字典
        # id在sku_ids里面
        skus = SKU.objects.filter(id__in=sku_ids)
    #     为了在序列化的时候可以带上count和selected
        for sku in skus:
            sku.count = cart_dict[sku.id]["count"]
            sku.selected=cart_dict[sku.id]["selected"]
    #     创建序列化器对象,进行序列化器操作
        serializer = serializers.CartSKUSerializer(skus,many=True)

        return Response(serializer.data)
    def put(self,request):
        #     使用序列化器校验参数
        serializer = serializers.CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        #     读取校验之后的参数
        sku_id = serializer.validated_data.get("sku_id")
        count = serializer.validated_data.get("count")
        selected = serializer.validated_data.get("selected")
        #     判断用户是否登录
        # request.user 会去查询用户的身份信息.如果未登录就会抛出异常或者指定为匿名用户
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            #         用户已登录,操作redis
            redis_conn = get_redis_connection("carts")
    #         修改商品的数量:因为接口设计幂等的,所以前端传入的数量就是最终的结果,直接覆盖
            redis_conn.hset("cart_%s"%user.id,sku_id,count)
    #         修改商品是否勾选:如果前端传入的是已勾选,那么由于使用的是幂等的接口设计,所以直接使用selected覆盖
            if selected:
                redis_conn.sadd("cart_selected_%s" %user.id,sku_id)
            else:
                redis_conn.srem("cart_selected_%s",user.id,sku_id)
                #         响应结果 (状态码:修改和获取是200  增加是201  删除是204)
            return Response(serializer.data)
        else:
            # 用户未登录,操作cookie
            cart_str = request.COOKIES.get("cart")
            #        判断用户是否存在cookie中有购物车数据
            if cart_str:
                #             说明用户在cookie中操作过购物车数据
                #             将原始的购物车字符串转成bytes类型的字符串(二进制字符串)
                #             cart_str.encode()
                #             将二进制的字符串,转换成base64编码后的二进制字符串
                #             base64.b64decode(cart_str.encode())
                #             将base64编码之后的二进制字符串转换成字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
                cart_dict[sku_id]={
                    "count" : count,
                    "selected" :selected
                }
                #             将二进制的字符串转成字符串
                cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                #             生成响应对象
                responce = Response(serializer.data, status=status.HTTP_201_CREATED)
                #             写入到cookie
                responce.set_cookie("cart", cookie_cart_str)

                return responce

    def delete(self,request):
        """
              删除购物车数据
              """
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data['sku_id']

        try:
            user = request.user
        except Exception:
            # 验证失败，用户未登录
            user = None

        if user is not None and user.is_authenticated:
            # 用户已登录，在redis中保存
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            pl.hdel('cart_%s' % user.id, sku_id)
            pl.srem('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            # 用户未登录，在cookie中保存
            response = Response(status=status.HTTP_204_NO_CONTENT)

            # 使用pickle序列化购物车数据，pickle操作的是bytes类型
            cart = request.COOKIES.get('cart')
            if cart is not None:
                cart = pickle.loads(base64.b64decode(cart.encode()))
                if sku_id in cart:
                    del cart[sku_id]
                    cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()
                    # 设置购物车的cookie
                    response.set_cookie('cart', cookie_cart)
            return response

class CartSelectAllView(APIView):
    """
    购物车全选
    """
    def perform_authentication(self, request):
        """
        重写父类的用户验证方法，不在进入视图前就检查JWT
        """
        pass

    def put(self, request):
        serializer = CartSelectAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data['selected']

        try:
            user = request.user
        except Exception:
            # 验证失败，用户未登录
            user = None

        if user is not None and user.is_authenticated:
            # 用户已登录，在redis中保存
            redis_conn = get_redis_connection('carts')
            cart = redis_conn.hgetall('cart_%s' % user.id)
            sku_id_list = cart.keys()

            if selected:
                # 全选
                redis_conn.sadd('cart_selected_%s' % user.id, *sku_id_list)
            else:
                # 取消全选
                redis_conn.srem('cart_selected_%s' % user.id, *sku_id_list)
            return Response({'message': 'OK'})
        else:
            # cookie
            cart = request.COOKIES.get('cart')

            response = Response({'message': 'OK'})

            if cart is not None:
                cart = pickle.loads(base64.b64decode(cart.encode()))
                for sku_id in cart:
                    cart[sku_id]['selected'] = selected
                cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()
                # 设置购物车的cookie
                # 需要设置有效期，否则是临时cookie
                response.set_cookie('cart', cookie_cart)

            return response

